try:
  from xml.etree import ElementTree # for Python 2.5 users
except ImportError:
  from elementtree import ElementTree
import gdata.calendar.service
import gdata.service
import atom.service
import gdata.calendar
import atom
import getopt
import sys
import string
import time
import wx
import urllib2
import getpass
import pickle
import hashlib
import datetime


#http://code.google.com/p/gdata-python-client/source/browse/trunk/samples/calendar/calendarExample.py

class talkToGoogle:
	# Function to authenticate user with system and with Google database
	def authenticate(self,user_name,user_password):
		self.user_name = user_name
		system_name = 'cse40232.scheduler'
		system_password = 'strongpassword'
		usrdata = open('usrdata.pkl','rb')
		password_lookup = pickle.load(usrdata)
		user_type_lookup = pickle.load(usrdata)
		usrdata.close()
		if self.user_name in password_lookup and self.user_name in user_type_lookup:
			if password_lookup[self.user_name] == hashlib.md5(user_password).hexdigest():
				if user_type_lookup[self.user_name] == 'user':
					self.valid_user = True
				elif user_type_lookup[self.user_name] == 'manager':
					self.valid_user = True
					self.manager = True
				else: return False
			else: return False
		else: return False
		
		if self.valid_user:
			self.calendar_service.email = system_name+'@gmail.com'
			self.calendar_service.password = system_password
			self.calendar_service.source = 'CSE40232-Scheduler-0.1'
			self.calendar_service.ProgrammaticLogin()
			print self.user_name,'successfully authenticated\n\n'
			return True
	
	# Logout the current user
	def logout(self):
		self.manager = False
		self.valid_user = False
		self.user_name = ''
		self.updateUsrdata()
		return False

	# Add user to authentication system
	def addUser(self,new_user_name, user_password, user_type):
		if user_type not in ['user','manager']:
			print 'user type bad'
			return False
		if self.manager:
			usrdata = open('usrdata.pkl','rb')
			password_lookup = pickle.load(usrdata)
			user_type_lookup = pickle.load(usrdata)
			usrdata.close()
			if new_user_name not in password_lookup and new_user_name not in user_type_lookup:
				password_lookup[new_user_name] = hashlib.md5(user_password).hexdigest()
				user_type_lookup[new_user_name] = user_type
				
				usrdata = open('usrdata.pkl','wb')
				pickle.dump(password_lookup, usrdata)
				pickle.dump(user_type_lookup, usrdata)
				usrdata.close()
				self.commitUsrdata()
				return True
			else: return False
		else: return False

	# Remove user from authentication system
	def removeUser(self,del_user_name):
		if self.manager:
			usrdata = open('usrdata.pkl','rb')
			password_lookup = pickle.load(usrdata)
			user_type_lookup = pickle.load(usrdata)
			usrdata.close()
			if del_user_name in password_lookup:
				del password_lookup[del_user_name]
			if del_user_name in user_type_lookup:
				del user_type_lookup[del_user_name]
			usrdata = open('usrdata.pkl','wb')
			pickle.dump(password_lookup, usrdata)
			pickle.dump(user_type_lookup, usrdata)
			usrdata.close()
			self.commitUsrdata()
			return True
		else: return False

	# Change user password
	def changePassword(self,chng_user_name, old_password, new_password):
		if self.valid_user:
			self.updateUsrdata()
			usrdata = open('usrdata.pkl','rb')
			password_lookup = pickle.load(usrdata)
			user_type_lookup = pickle.load(usrdata)
			usrdata.close()
			if chng_user_name in password_lookup and chng_user_name in user_type_lookup:
				if password_lookup[chng_user_name] == hashlib.md5(old_password).hexdigest():
					password_lookup[chng_user_name] = hashlib.md5(new_password).hexdigest()
					usrdata = open('usrdata.pkl','wb')
					pickle.dump(password_lookup, usrdata)
					pickle.dump(user_type_lookup, usrdata)
					usrdata.close()
					self.commitUsrdata()
					return True
				else: return False
			else: return False
		else: return False
  

	# Retrieve existing schedule for single user
	def acquireEmployeeEvents(self,current_user_name, additional_query='',start_time = str(time.localtime()[0]-1)+'-01-01', stop_time =  str(time.localtime()[0]+1)+'-12-31'):
		if self.valid_user:
			events = []
			#Look from one year back to one year ahead, unless start_time and stop_time are defined otherwise
			query = gdata.calendar.service.CalendarEventQuery('default', 'private', 'full', current_user_name+' '+additional_query)
			query.start_min = start_time
			query.start_max = stop_time
			feed = self.calendar_service.CalendarQuery(query)
			for i, an_event in enumerate(feed.entry):
				event_list_entry = []
				event_list_entry.append(an_event.title.text)
				event_list_entry.append(an_event.content.text)
				event_list_entry.append(an_event.GetEditLink().href)
				#print '\t%s. %s' % (i, an_event.title.text,)
				#print '\t\t%s. %s' % (i, an_event.content.text,)
				#print '\t\t%s. %s' % (i, an_event.GetEditLink().href,)
				event_start_time = ''
				event_stop_time = ''
				for a_when in an_event.when:
					event_start_time = a_when.start_time
					event_end_time = a_when.end_time
					#print '\t\tStart time: %s' % (a_when.start_time,)
					#print '\t\tEnd time:   %s' % (a_when.end_time,)
				event_start_time_first, event_start_time_second = stringToTime(event_start_time)
				event_end_time_first, event_end_time_second = stringToTime(event_end_time)
				event_list_entry.append([event_start_time_first,event_start_time_second])
				event_list_entry.append([event_end_time_first,event_end_time_second])
				events.append(event_list_entry)
			return events
		else: return False
  
	# Add single event for single user
	def addEvent(self,current_user_name, start_time, end_time, event_description='',location=''):
		event = gdata.calendar.CalendarEventEntry()
		event.title = atom.Title(text=current_user_name)
		event.content = atom.Content(text=event_description)
		event.where.append(gdata.calendar.Where(value_string=location))
		event.when.append(gdata.calendar.When(start_time=start_time, end_time=end_time))
		try:
			new_event = self.calendar_service.InsertEvent(event, '/calendar/feeds/default/private/full')
		except: return False
		return True
	
	# Add weekly event for single user
	def addWeeklyEvent(self,current_user_name, start_time, end_time, event_description='',location=''):
		day_names = ['Mo','Tu','We','Th','Fr','Sa','Su']
		start_time_object, start_time_object_offset = stringToTime(start_time)		
		event = gdata.calendar.CalendarEventEntry()
		event.title = atom.Title(text=current_user_name)
		event.content = atom.Content(text=event_description)
		event.where.append(gdata.calendar.Where(value_string=location))
		#event.when.append(gdata.calendar.When(start_time=start_time, end_time=end_time))
		event.recurrence = gdata.calendar.Recurrence(text= ('DTSTART:'+start_time[0:4]+start_time[5:7]+start_time[8:10]+'T'+start_time[11:13]+start_time[14:16]+start_time[17:19]+'\r\n'
        + 'DTEND:'+end_time[0:4]+end_time[5:7]+end_time[8:10]+'T'+end_time[11:13]+end_time[14:16]+end_time[17:19]+'\r\n'
        + 'RRULE:FREQ=WEEKLY;BYDAY='+day_names[start_time_object.weekday()]))
		#+';UNTIL=20070904\r\n')
		try:
			new_event = self.calendar_service.InsertEvent(event, '/calendar/feeds/default/private/full')
		except:
			return False
		return True
		
	# Remove range of events for single user in given time period.  Will delete any event that overlaps with this time period
	def removeRangeEvents(self,current_user_name, start_time, stop_time, event_description='',location=''):
		if self.valid_user and start_time != '' and stop_time != '':
			event_reference = []
			query = gdata.calendar.service.CalendarEventQuery('default', 'private', 'full', current_user_name+' '+event_description+' '+location)
			query.start_min = start_time
			query.start_max = stop_time
			try:
				feed = self.calendar_service.CalendarQuery(query)
			except:
				return False
			for i, an_event in enumerate(feed.entry):
				event_reference.append(an_event.GetEditLink().href)
			for i in event_reference:
				self.removeEvent_href(i)
			return True
		else:
			return False

	# Remove event for single user (or multiple events if they occur on the exact same start and stop times)
	def removeEvent(self,current_user_name, start_time, stop_time, event_description='', location=''):
		if self.valid_user and start_time != '' and stop_time != '':
			event_reference = []
			query = gdata.calendar.service.CalendarEventQuery('default', 'private', 'full', current_user_name+' '+event_description+' '+location)
			query.start_min = start_time
			query.start_max = stop_time
			try:
				feed = self.calendar_service.CalendarQuery(query)
			except:
				return False
			for i, an_event in enumerate(feed.entry):
				for a_when in an_event.when:
					event_start_time, event_start_time_offset = stringToTime(a_when.start_time)
					input_start_time, input_start_time_offset = stringToTime(start_time)
					event_stop_time, event_stop_time_offset = stringToTime(a_when.end_time)
					input_stop_time, input_stop_time_offset = stringToTime(stop_time)
					if event_start_time_offset != input_start_time_offset:
						event_start_time, event_start_time_offset = stringToTime(timeToString(event_start_time,-1*event_start_time_offset))
						input_start_time, input_start_time_offset = stringToTime(timeToString(input_start_time,-1*input_start_time_offset))
					if event_stop_time_offset != input_stop_time_offset:
						event_stop_time, event_stop_time_offset = stringToTime(timeToString(event_stop_time,-1*event_stop_time_offset))
						input_stop_time, input_stop_time_offset = stringToTime(timeToString(input_stop_time,-1*input_stop_time_offset))
					if event_start_time == input_start_time and event_stop_time == input_stop_time:
						print 'found it'
						event_reference.append(an_event.GetEditLink().href)
			for i in event_reference:
				self.removeEvent_href(i)
		else:
			return  False
		
	# Remove single event using the unique href
	def removeEvent_href(self,href):
		try:
			self.calendar_service.DeleteEvent(href)
			return True
		except:
			print 'Delete failed, due to connection error'
			return False
		
	# Acquire updated password info
	def updateUsrdata(self):
		password_service = gdata.calendar.service.CalendarService()
		password_service.email = 'cse40232.scheduler.pass@gmail.com'
		password_service.password = 'strongpassword'
		password_service.source = 'CSE40232-Scheduler-0.1'
		try:
			password_service.ProgrammaticLogin()
		except:
			return False
		query = gdata.calendar.service.CalendarEventQuery('default', 'private', 'full', 'administrivia')
		query.start_min = '2009-01-01T11:00:00.000Z'
		query.start_max = '2009-01-01T14:00:00.000Z'
		try:
			feed = password_service.CalendarQuery(query)
		except:
			return False
		usrdata_data = ''
		for i, an_event in enumerate(feed.entry):
			for a_when in an_event.when:
				if an_event.title.text == 'administrivia':
					usrdata_data = an_event.content.text
					break
		try:
			usrdata = open('usrdata.pkl','wb')
			usrdata.write(usrdata_data)
			usrdata.close()
		except:
			return False
		return True
	
	def commitUsrdata(self):
		password_service = gdata.calendar.service.CalendarService()
		password_service.email = 'cse40232.scheduler.pass@gmail.com'
		password_service.password = 'strongpassword'
		password_service.source = 'CSE40232-Scheduler-0.1'
		try:
			password_service.ProgrammaticLogin()
		except:
			return False
		usrdata_data = ''
		try:
			usrdata = open('usrdata.pkl','rb')
			usrdata_data = usrdata.read()
			usrdata.close()
		except:
			return False
		query = gdata.calendar.service.CalendarEventQuery('default', 'private', 'full', 'administrivia')
		query.start_min = '2009-01-01T11:00:00.000Z'
		query.start_max = '2009-01-01T14:00:00.000Z'
		feed = password_service.CalendarQuery(query)
		for i, an_event in enumerate(feed.entry):
			if an_event.title.text == 'administrivia':
				an_event.content.text = usrdata_data
				try:
					password_service.UpdateEvent(an_event.GetEditLink().href, an_event)
				except:
					return False
				
	def __init__(self):
		self.updateUsrdata()
	
	manager = False
	valid_user = False
	user_name = ''
	calendar_service = gdata.calendar.service.CalendarService()
		

# Returns string in propper google calendar format from python time object
def timeToString(time_object, offset_hours = 0):
	try:
		time_object += datetime.timedelta(hours = offset_hours)
		output_string = str(time_object.year)+'-'
		if time_object.month < 10:
			output_string += '0'+str(time_object.month)+'-'
		else:
			output_string += str(time_object.month)+'-'
		if time_object.day < 10:
			output_string += '0'+str(time_object.day)+'T'
		else:
			output_string += str(time_object.day)+'T'
		if time_object.hour < 10:
			output_string += '0'+str(time_object.hour)+':'
		else:
			output_string += str(time_object.hour)+':'
		if time_object.minute < 10:
			output_string += '0'+str(time_object.minute)+':'
		else:
			output_string += str(time_object.minute)+':'
		if time_object.second < 10:
			output_string += '0'+str(time_object.second)+'.000Z'
		else:
			output_string += str(time_object.second)+'.000Z'
		return output_string
	except:
		return False

# Returns time object and timezone offset from google calendar time string
def stringToTime(input_string):
	try:
		time_object = datetime.datetime(2009,1,1)
		time_object = time_object.replace(year = int(input_string[0:4]))
		if input_string[5:7].lstrip('0') != '':
			time_object = time_object.replace(month = int(input_string[5:7].lstrip('0')))
		if input_string[8:10].lstrip('0') != '':
			time_object = time_object.replace(day = int(input_string[8:10].lstrip('0')))
		if input_string[11:13].lstrip('0') != '':
			time_object = time_object.replace(hour = int(input_string[11:13].lstrip('0')))
		if input_string[14:16].lstrip('0') != '':
			time_object = time_object.replace(minute = int(input_string[14:16].lstrip('0')))
		if input_string[17:19].lstrip('0') != '':
			time_object = time_object.replace(second = int(input_string[17:19].lstrip('0')))
		offset = 0
		if input_string[-1] != 'Z' and len(input_string) == 24:
			adjustment = input_string[-5:]
			adjustment_data = adjustment.split(':')
			offset = float(adjustment_data[0].lstrip('0'))
			if adjustment_data[1].lstrip('0') != '':
				offset += (float(adjustment_data[1].lstrip('0'))/60.0)
			if input_string[-6] == '-':
				offset *= -1
		return time_object, offset
	except:
		return False, False
